﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DotLiquid;

namespace ThirdPartyLib.DotLiquidTemplatingDemo.Services
{
    public interface ITemplatingService
    {
        /// <summary>
        /// Populates a DotLiquid template with values from a model 
        /// </summary>
        string Render(string template, object model);
    }

    public class TemplatingService : ITemplatingService
    {
        public string Render(string template, object model)
        {
            var liquidHash = ToLiquidHash(model);
            return Template.Parse(template).Render(liquidHash);
        }

        /// <summary>
        /// Recursively turns an object into a dictionary list of property names and values
        /// </summary>
        protected static Dictionary<string, object> ToDictionary(object model)
        {
            var hash = new Dictionary<string, object>();
            if (model == null) return hash;

            //Get list of properties on object (leave here so it's easy to breakpoint)
            var props = model.GetType().GetProperties();

            foreach (var property in props)
            {
                //Get type and value for property
                var propType = property.PropertyType;
                var val = property.GetValue(model, null);

                //If its an enum
                if (propType.IsEnum)
                {
                    hash[property.Name] = Enum.GetName(propType, val);
                }
                //If primitive (non-complex type) than add to hash
                else if (propType.IsPrimitive || propType.IsValueType || (propType == typeof (string)))
                {
                    hash[property.Name] = val;
                }

                //If it's an array of a primitive type just add to hash, if array of complex type get the dictionary for each item in list
                else if (propType.GetInterface(typeof (IEnumerable).Name) != null ||
                         propType.GetInterface(typeof (IEnumerable<>).Name) != null)
                {
                    var arrayType = propType.GetGenericArguments().First();

                    if (arrayType.IsPrimitive || arrayType.IsValueType || (arrayType == typeof (string)))
                    {
                        hash[property.Name] = val;
                    }
                    else
                    {
                        var x = (from object v in (IEnumerable) val select ToDictionary(v)).ToArray();
                        hash[property.Name] = x;
                    }
                }
                
                //If it's a complex type add the dictionary to the hash
                else
                {
                    hash[property.Name] = ToDictionary(val);
                }
            }
            return hash;
        }

        /// <summary>
        /// Converts a model to DotLiquid Hash compatible dictionary
        /// </summary>
        protected static Hash ToLiquidHash(object model)
        {
            return model == null ? new Hash() : Hash.FromDictionary(ToDictionary(model));
        }
    }
}
